package com.cyandpos.pad.http;

import android.os.Handler;
import android.os.Looper;
import android.util.Base64;
import android.util.Log;

import com.cyandpos.pad.common.commondata.ErpCommonData;
import com.cyandpos.pad.utils.log.NetWorkLog;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;

import org.apache.http.conn.ConnectTimeoutException;

import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;


/**
 * Created by yangzw on 2017/2/25.
 */

public class OkHttpUtils {
    private static OkHttpUtils mInstance;
    private OkHttpClient mOkHttpClient;
    private OkHttpClient.Builder mOkHttpClientBuilder;
    private Handler mHandler;
    private static int TIMEOUT = 60;//超时时长  单位 秒
    private Gson GSON;
    private static String uniqueID = "";
    public static ArrayList<Interceptor> INTERCEPTORS = new ArrayList<Interceptor>();//拦截器list
    private String strA = "";
    private String strB = "";
    private String p1 = "";
    private String p2 = "";
    private String n1 = "";
    private String d1 = "";
    private String key = "";
    private String iv = "";
    private String name = "cypos";
    private String tips;
    private OkHttpUtils() {

//        okhttp3.0之后：
        if (INTERCEPTORS != null && !INTERCEPTORS.isEmpty()) {
            for (Interceptor interceptor : INTERCEPTORS) {
                mOkHttpClient = new OkHttpClient().newBuilder()
                        .connectTimeout(TIMEOUT, TimeUnit.SECONDS)
                        .writeTimeout(TIMEOUT, TimeUnit.SECONDS)
                        .readTimeout(TIMEOUT, TimeUnit.SECONDS)
                        .addInterceptor(interceptor).build();
            }
        }else{
            mOkHttpClient = new OkHttpClient().newBuilder()
                    .connectTimeout(TIMEOUT, TimeUnit.SECONDS)
                    .writeTimeout(TIMEOUT, TimeUnit.SECONDS)
                    .readTimeout(TIMEOUT, TimeUnit.SECONDS).build();
        }

        mHandler = new Handler(Looper.getMainLooper());
        GSON = new Gson();
    }

    private synchronized static OkHttpUtils getmInstance() {
        if (mInstance == null) {
            mInstance = new OkHttpUtils();
        }
        return mInstance;
    }

    /********************
     * 对外的接口
     **********************/

    public static void get(String url, ResultCallback callback) {
        getmInstance().getRequest(url, callback);
    }

    public static void post(String url, Map<String,Object> params, ResultCallback callback) {
        if(url.isEmpty()){
            callback.onFailure("请求地址不能为空");
            return;
        }
        //Guid-请求id
        if(uniqueID.equals("")){
            uniqueID = UUID.randomUUID().toString();
        }
        params.put("requestId",uniqueID);
        NetWorkLog.writeLog(createHttpLog( "地址："+url+params.toString(),0));
        getmInstance().postRequest(url, callback, params);
    }

    /****
     * 对外的接口
     ****/
    private void getRequest(String url, ResultCallback callback) {
        Request request = new Request.Builder().url(url).build();
        deliverResult(callback, request);
    }

    private void postRequest(String url, ResultCallback callback, Map<String,Object> params) {
        Request request = buildPostRequest(url, params);
        deliverResult(callback, request);
    }

    private Request buildPostRequest(String url, Map<String,Object> params) {
        String sign = getStringBuffer(params);
        params.put("sign",sign);
        params.remove("Params_json_sign");
        String params_json = GSON.toJson(params.get("Params_json"));
        if(ErpCommonData.isDataEncrypted){
            String md5_key = getMd5_key(sign);
            String md5_iv = getMd5_iv(sign);
            String submd5_key = md5_key.substring(md5_key.length() - 16, md5_key.length());
            String submd5_iv = md5_iv.substring(md5_iv.length() - 16, md5_iv.length());
            String s = encryptCBC(params_json, submd5_key, submd5_iv, sign);
            params.put("Params_json", s);
        }else{
            params.put("Params_json", params_json);
        }
        String jsonStr = GSON.toJson(params);
        MediaType JSON = MediaType.parse("application/json; charset=utf-8");
        RequestBody rb  = RequestBody.create(JSON,jsonStr);
        return new Request.Builder().url(url).post(rb).build();
    }

    private static String getStringBuffer(Map<String,Object> params) {
        StringBuffer str = new StringBuffer();
        str.append(ErpCommonData.keySercet);
        str.append("format");
        str.append(params.get("format"));
        str.append("Params_json");
        str.append(params.get("Params_json_sign"));
//        str.append(params.get("Params_json"));
        str.append("sys_key");
        str.append(params.get("sys_key"));
        str.append("timestamp");
        str.append(params.get("timestamp"));
//        str.append("token");
//        str.append(params.get("token"));
        str.append("version");
        str.append(params.get("version"));
        str.append(ErpCommonData.keySercet);
        return md5(str.toString()).toUpperCase();
    }

    public static String md5(String src){
        String session ="";
        try{
            MessageDigest md5= MessageDigest.getInstance("md5");//返回实现指定摘要算法的 MessageDigest 对象。
            md5.update(src.getBytes());//先将字符串转换成byte数组，再用byte 数组更新摘要
            byte[] nStr = md5.digest();//哈希计算，即加密
            session = bytes2Hex(nStr);//加密的结果是byte数组，将byte数组转换成字符串
        }catch(Exception e){
            Log.i("AAA","密码加密失败" +e.getMessage());
        }
        Log.i("AAA","加密后的结果为：" +session);
        return session;
    }

    public static String bytes2Hex(byte[] bts) {
        String des = "";
        String tmp = null;
        for (int i = 0; i < bts.length; i++) {
            tmp = (Integer.toHexString(bts[i] & 0xFF));
            if (tmp.length() == 1) {
                des += "0";
            }
            des += tmp;
        }
        return des;
    }

    private void deliverResult(final ResultCallback callback, final Request request) {

        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                if(e instanceof SocketTimeoutException){
                    sendFailureCallback(callback, "连接超时");
                }else if(e instanceof ConnectException){
                    sendFailureCallback(callback, "网络不可用");
                }else if( e instanceof ConnectTimeoutException){
                    sendFailureCallback(callback, "连接超时");
                }else if(e instanceof UnknownHostException){
                    sendFailureCallback(callback, "连不到服务器");
                }else{
                    sendFailureCallback(callback, e.toString());
                }
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {  //状态码 >=200  <300
                    if(response.code()==200) {
                        uniqueID = "";
                        String result = getResponse(response);
                        try {
                            JsonObject jsonObject = new JsonParser().parse(result).getAsJsonObject();

                            if(ErpCommonData.isDataEncrypted){
                                String object=  jsonObject.get("Data").toString();
                                if (object!=null&& !object.equals("null")&&!object.isEmpty()){

                                    String code=  jsonObject.get("Code").toString();
                                    tips = jsonObject.get("tips").toString();
                                    String msg=  jsonObject.get("Msg").toString();
                                    String session = object.substring(1,33);
                                    session=  md5(session);
                                    String md5_key = getMd5_key(session);
                                    String md5_iv = getMd5_iv(session);
                                    String submd5_key = md5_key.substring(md5_key.length() - 16, md5_key.length());
                                    String submd5_iv = md5_iv.substring(md5_iv.length() - 16, md5_iv.length());

                                    String  result1 = decryptCBC(object.toString().substring(33), submd5_key,submd5_iv);

                                    String  jsonGoods =  "{\"Code\":" + code
                                            + ",\"Tips\":" + tips
                                            + ",\"Msg\":" + msg
                                            + ",\"Data\":" + result1
                                            + "}";

                                    JsonObject jsonObject1 = new JsonParser().parse(jsonGoods).getAsJsonObject();
                                    sendSuccessCallback(callback, jsonObject1);

                                }else{
                                    sendSuccessCallback(callback, jsonObject);
                                }
                            }else{
                                sendSuccessCallback(callback, jsonObject);
                            }
                        }catch (JsonParseException e){
                            sendFailureCallback(callback, "无效的Json字符串");
                        }
                    }else{
                        uniqueID = "";
                        sendFailureCallback(callback, "网络错误:"+"状态码"+response.code()+"-"+response.message());
                    }
                } else {
                    sendFailureCallback(callback, "网络错误:"+"状态码"+response.code()+"-"+response.message());
                }
            }
        });

    }


    private void sendFailureCallback(final ResultCallback callback, final String e) {
        NetWorkLog.writeLog(createHttpLog(e,1));
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (callback != null) {
                    callback.onFailure(e);
                }
            }
        });
    }

    private void sendSuccessCallback(final ResultCallback callback, final JsonObject jsonStr) {
        NetWorkLog.writeLog(createHttpLog(jsonStr.toString(),1));
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (callback != null) {
                    callback.onSuccess(jsonStr);
                }
            }
        });
    }

    public static void setTIMEOUT(int TIMEOUT) {
        OkHttpUtils.TIMEOUT = TIMEOUT;
    }

    public static abstract class ResultCallback<T> {

        public ResultCallback() {

        }

        public abstract void onSuccess(JsonObject response);

        public abstract void onFailure(String msg);
    }

    private static String createHttpLog(String content, int type){
        if (type == 0)
            return getCurrentTime() +"请求：" +content;
        else
            return getCurrentTime() +"响应：" +content;
    }



    private static String getCurrentDate(){
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        Date curDate = new Date(System.currentTimeMillis());//获取当前时间
        return formatter.format(curDate);
    }

    public static String getCurrentTime(){
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ");
        Date curDate = new Date(System.currentTimeMillis());//获取当前时间
        return formatter.format(curDate);
    }

    public static void addInterceptor(Interceptor interceptor) {
        INTERCEPTORS.add(interceptor);
    }

    public String getResponse(Response response){
        String result = "";

        try {
            return response.body().string();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    public String getMd5_key(String session){

        strB = session.substring(0,16);
        p1 = strB.substring(0,4);
        p2= strB.substring(strB.length()-4,strB.length());
        n1= name.substring(name.length()-4,name.length());
        d1= new SimpleDateFormat("MMdd").format(Calendar.getInstance().getTime());

        key = p1 + d1 + n1 + p2;
        iv = p2 + n1 + d1 + p1;
        return md5(key).substring(16);
    }

    public String getMd5_iv(String session){
        strB = session.substring(0,16);
        p1 = strB.substring(0,4);
        p2= strB.substring(strB.length()-4,strB.length());
        n1= name.substring(name.length()-4,name.length());
        d1= new SimpleDateFormat("MMdd").format(Calendar.getInstance().getTime());

        key = p1 + d1 + n1 + p2;
        iv = p2 + n1 + d1 + p1;
        return md5(iv).substring(16);
    }

    public static String encryptCBC(String encryptStr,String key,String iv,String session) {
        //构建SecretKeySpec，初始化Cipher对象时需要该参数
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES");
        try {
            //构建Cipher对象，需要传入一个字符串，格式必须为"algorithm/mode/padding"或者"algorithm/",意为"算法/加密模式/填充方式"
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            //初始化Cipher对象
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec,ivParameterSpec);
            //加密数据
            byte[] resultBytes = cipher.doFinal(encryptStr.getBytes());
            //结果用Base64转码
            String finalResult = Base64.encodeToString(resultBytes,Base64.DEFAULT);
//            String finalResultSession = Base64.encodeToString(resultBytesSession,Base64.DEFAULT);
            return  session + finalResult;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String decryptCBC(String encryptStr,String key,String iv) {
        //构建SecretKeySpec，初始化Cipher对象时需要该参数
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES");
        try {
            //构建Cipher对象，需要传入一个字符串，格式必须为"algorithm/mode/padding"或者"algorithm/",意为"算法/加密模式/填充方式"
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            //初始化Cipher对象
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec,ivParameterSpec);
            //加密数据
            byte[] decode = Base64.decode(encryptStr.getBytes(), Base64.DEFAULT);
            byte[] resultBytes = cipher.doFinal(decode);

            //结果用Base64转码
//            String finalResult = Base64.encodeToString(resultBytes,Base64.DEFAULT);
            String finalResult = new String(resultBytes);
            return  finalResult;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
        return null;
    }
}
